package com.laguille.games.monopoly.server;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.apache.commons.lang.Validate;

import com.laguille.games.monopoly.commons.board.Board;
import com.laguille.games.monopoly.commons.event.DiceResultEvent;
import com.laguille.games.monopoly.commons.event.EmptyPropertyEvent;
import com.laguille.games.monopoly.commons.event.Event;
import com.laguille.games.monopoly.commons.event.InsufficientFundsEvent;
import com.laguille.games.monopoly.commons.event.NewPlayerEvent;
import com.laguille.games.monopoly.commons.event.NextPlayerTurnEvent;
import com.laguille.games.monopoly.commons.event.PlayerAtHomeEvent;
import com.laguille.games.monopoly.commons.event.PlayerBuyPropertyEvent;
import com.laguille.games.monopoly.commons.event.PlayerPayRentEvent;
import com.laguille.games.monopoly.commons.event.StartingGameEvent;
import com.laguille.games.monopoly.commons.event.UnfinishedTurnEvent;
import com.laguille.games.monopoly.commons.player.Player;
import com.laguille.games.monopoly.commons.property.Property;

public class GameControllerImpl implements GameController {

	private final static Random intRandom = new Random();

	private final GameContext context;
	private final EventBus eventBus;
	private final List<Player> registeredPlayers;
	private boolean gameStarted = false;
	
	public GameControllerImpl(EventBus eventBus, GameContext context) {
		this.eventBus = eventBus;
		this.context = context;
		this.registeredPlayers = new ArrayList<Player>();
	}

	@Override
	public void registerPlayer(Player player) {
		Validate.notNull(player);
		Validate.isTrue(!registeredPlayers.contains(player));

		if (gameStarted) {
			throw new IllegalStateException("Can not register a player after the game is started.");
		}
		
		registeredPlayers.add(player);
		eventBus.registerPlayer(player);
		context.registerPlayer(player);
		
		NewPlayerEvent event = new NewPlayerEvent(player);
		eventBus.pushUniversalEvent(event);
	}
	
	@Override
	public void startGame() {

		if (gameStarted) {
			throw new IllegalStateException("The game already started");
		}
		
		if (registeredPlayers.isEmpty()) {
			throw new IllegalStateException("Can not start a game without any registered players.");
		}

		gameStarted = true;
		
		Event event = new StartingGameEvent();
		eventBus.pushUniversalEvent(event);
	}
	
	@Override
	public void rollDice(Player player) {
		Validate.notNull(player);
		Validate.isTrue(gameStarted);
		
		int diceOne = intRandom.nextInt(DiceResultEvent.MAX_THROW_VALUE) + 1;
		int diceTwo = intRandom.nextInt(DiceResultEvent.MAX_THROW_VALUE) + 1;

		DiceResultEvent diceResultEvent = new DiceResultEvent(player, diceOne, diceTwo);
		eventBus.pushUniversalEvent(diceResultEvent);
		
		Board board = context.getBoard();
		board.movePlayer(player, diceOne + diceTwo);
//		board.getPlayerPosition(player).onLanding(context, eventBus);
		onLanding(player, board.getPlayerPosition(player));
	}

	@Override
	public void buyProperty(Player player, Property property) {
		
		Validate.notNull(player);
		Validate.notNull(property);
		Validate.isTrue(context.getOwner(property) == null);
		Validate.isTrue(gameStarted);
		
		if (player.buyProperty(property)) {
			
			Event playerBuyPropertyEvent = new PlayerBuyPropertyEvent(player, property);
			eventBus.pushUniversalEvent(playerBuyPropertyEvent);
			
		} else {
			
			Event insufficientFundsEvent = new InsufficientFundsEvent(player, property.getBuyingPrice());
			eventBus.pushEvent(insufficientFundsEvent, player);
			
		}
	}

	@Override
	public void endTurn(Player player) {
		Validate.notNull(player);
		Validate.isTrue(gameStarted);
		
		if (player.getCash() >= 0) { 
			
			Player oldPlayer = player;
			context.nextPlayer();
			Event nextPlayerTurnEvent = new NextPlayerTurnEvent(context.getCurrentPlayer(), oldPlayer);
			eventBus.pushUniversalEvent(nextPlayerTurnEvent);
		
		} else {
		
			Event unfinishedTurnEvent = new UnfinishedTurnEvent(player);
			eventBus.pushEvent(unfinishedTurnEvent, player);
		}
	}

	@Override
	public EventBus getEventBus() {
		return eventBus;
	}

	private void onLanding(Player player, Property property) {
		
		Player owner = context.getOwner(property);
		Player currentPlayer = context.getCurrentPlayer();
		
		if (owner == null) {
			
			EmptyPropertyEvent emptyPropEvent = new EmptyPropertyEvent(currentPlayer, property);
			eventBus.pushEvent(emptyPropEvent, currentPlayer);
			
		} else if (!owner.equals(currentPlayer)) {

			int rent = property.getDefaultRentingPrice();
			currentPlayer.withdrawCash(rent);
			owner.depositCash(rent);
			PlayerPayRentEvent payRentEvent = new PlayerPayRentEvent(currentPlayer, owner, property, rent);
			eventBus.pushUniversalEvent(payRentEvent);
			
		} else {

			PlayerAtHomeEvent atHomeEvent = new PlayerAtHomeEvent(currentPlayer, property);
			eventBus.pushUniversalEvent(atHomeEvent);
			
		}
	}

}
